﻿package org.papervision3d.objects.special 
{
	import org.papervision3d.core.animation.channel.AbstractChannel3D;
	import org.papervision3d.core.render.data.RenderSessionData;
	import org.papervision3d.events.FileLoadEvent;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.parsers.DAE;
	import flash.utils.getTimer;
	
	/**
	 *
	*  DAEMC = DAE like a MovieClip
	*  usage is the same as with a DAE but you have more control over animation with:
	* 
	* 	-gotoAndStop(frameLabel or frameNumber)
	* 	-gotoAndPlay(frameLabel or frameNumber)
	* 	-addFrameScript(frameNumber, callbackFunction)
	* 	-setFrameLabel(frameNumber, frameLabel)
	* 	-isPlaying :property, returns true or false if is playing or not
	* 
	* 
	* @author Pablo Bandin (p48l0.84nd1n@gmail.com)
	* @lastupdate 26/Abr/2009
	* @proyecthome http://code.google.com/p/daeanim/
	* @version fix6
	*/
	
	public class DAEMC extends DAE {
		
		/** the FPS used by this DAE object */
		protected var __fps:uint;
		
		/** just place holder fps/1000 so it dosent get calculated each frame */
		protected var __fps1000:Number;
		
		/** */
		protected var __timelineID:Number;
		
		/** */
		protected var _lastRenderedTime:Number;
		
		/** */
		protected var __currentFrame:Number;
		
		/** */
		protected var __frameLabels:Object;
		
		/** */
		protected var __frameActions:Object;
		
		/** */
		protected var __lastFrameAction:int;
		
		/** */
		protected var __autoPlay:Boolean;
		
		/** */
		protected var __canPlay:Boolean;
		
		/** */
		protected var __stopAtFrame:int;
		
		/**
		 * Constructor.
		 * 
		 * @param	autoPlay	Whether to start the animation automatically.
		 * @param	name	Optional name for the DAE.
		 * @param   fps   Whether to use a custom frame rate, or render at enter frame only.
		 */ 
		public function DAEMC(autoPlay:Boolean = true, name:String = null, fps:uint = 0) 
		{
			this.fps			= fps;
			__lastFrameAction 	= -1;
			__stopAtFrame		= -1;
			__frameActions		= { };
			__frameLabels		= { };
			__currentFrame		= 0;
			
			__canPlay 			= false;
			__autoPlay			= autoPlay;
			addEventListener(FileLoadEvent.ANIMATIONS_COMPLETE, onAnimationsComplete, false, Number.MAX_VALUE, true);
			
			super(false, name, false);
		}
		
		/**
		 * Sets the model Frames per second
		 */
		public function set fps(newFPS:uint):void 
		{
			__fps = newFPS;
			__fps1000 = __fps / 1000;
		}
		
		/**
		 * returns the frames per second
		 */
		override public function get fps():uint { return __fps;  }
		
		/**
		 * @internal
		 */
		protected function onAnimationsComplete(e:FileLoadEvent):void 
		{
			if (__autoPlay)
				play();
		}
		
		public function get totalFrames():uint {
			return _totalFrames;
		}
		
		public function get currentFrame():uint {
			return uint(__currentFrame);
		}
		
		override public function play(clip:String = null):void {
			__canPlay = true;
			__stopAtFrame = -1;
		}
		override public function stop():void {
			__canPlay = false;
			_lastRenderedTime = 0;
		}
		
		override public function project(parent:DisplayObject3D, renderSessionData:RenderSessionData):Number 
		{
			if (_totalFrames > 0 && __canPlay)
			{
				
				for each(var channel:AbstractChannel3D in _channels)
					channel.updateToFrame(uint(__currentFrame));
					
				if (__stopAtFrame > -1)
				{
					__stopAtFrame = -1;
					stop();
				}
				else 
				{
					if (__fps)
					{
							var elapsed:Number 	= _lastRenderedTime?(getTimer() - _lastRenderedTime):0;
							var frames:Number 	=  elapsed * __fps1000 ;
							_lastRenderedTime	= getTimer();
							__timelineID		= getTimer();
							
							var tid:Number		= __timelineID;
							
							for (var i:uint = 0; i < uint(frames); i++ )
							{
								__currentFrame++;
								
								if (__currentFrame >= _totalFrames)
								__currentFrame = 0;
								
								//trace('Frame: ' + uint(__currentFrame), '('+__currentFrame+')')
								executeFrameAction(__currentFrame)
								
								if (__timelineID != tid) {
									frames = 0;
									break;
								}
							}
							
							__currentFrame += frames - uint(frames)
							if (__currentFrame >= _totalFrames)
								__currentFrame = 0;
							
							//trace('Frame: ' + uint(__currentFrame), '('+__currentFrame+')')
							executeFrameAction(__currentFrame);
					}
					else 
					{
							if (__lastFrameAction > -1) 
							{
								
								if (++__currentFrame >= _totalFrames)
								{
									__currentFrame = 0;
									
								}
							}
									
							//trace('Frame: ' + uint(__currentFrame), '('+__currentFrame+')')	
							executeFrameAction(__currentFrame)
					}
				}
			}
			
			return super.project(parent, renderSessionData);
		}
		
		
		protected function executeFrameAction(frame:Number):void 
		{
			var frm:int = int(frame);
			if (__lastFrameAction == frm)
				return;
				
			__lastFrameAction = frm;
			
			if (__frameActions[frm])
				__frameActions[frm](this);
		}
		
		public function setFrameLabel(frame:uint, name:String = null):void 
		{
			__frameLabels[name] = frame;
		}
		
		/**
		 * @internal
		 */
		protected function gotoFrame(frame:Object):Number 
		{
			__timelineID = getTimer();
			
			if (frame is Number)
			{
				__currentFrame = Number(frame);
			}
			else if (frame is String)
			{
				if (__frameLabels[String(frame)]!=null)
					__currentFrame = __frameLabels[String(frame)];
					
			}
			
			return __currentFrame;
		}
		
		/**
		 * frame can be a Number o String
		 */
		public function gotoAndPlay(frame:Object):void 
		{
			play();
			gotoFrame(frame)
		}
		
		/**
		 * frame can be a Number o String
		 */
		public function gotoAndStop(frame:Object):void 
		{
			__stopAtFrame = gotoFrame(frame);
		}
		
		public function addFrameScript(frame:uint, callback:Function = null):void 
		{
			__frameActions[frame] = callback;
		}
		
		/**
		 * returns whether or not an animation is currently playing
		 */
		public function get isPlaying():Boolean
		{
			return __canPlay;
		}
		
	}
	
}